{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Enabling licensed solvers on Deepnote\n",
    "For instructions on how to run these tutorial notebooks, please see the [index](./index.ipynb).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "assert \"DEEPNOTE_PROJECT_ID\" in os.environ, \"This tutorial is meant to be run on Deepnote\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Drake's MathematicalProgram interface supports some commercial solvers that require users to provide a license file in order to activate them.  This tutorial provides an example of how this can be accomplished on Deepnote; attempting to simplify the workflow but also minimize the chances of you accidentally sharing your license file.\n",
    "\n",
    "You can **duplicate this notebook**, and upload your license file, and run the code to make sure the activation works."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Mosek\n",
    "\n",
    "* If you don't have one, obtain a Mosek license. If you have academic status, then you can obtain a [free personal academic license](https://www.mosek.com/products/academic-licenses/).\n",
    "* This cell will prompt you to upload the `mosek.lic` file from your local machine.\n",
    "\n",
    "**Please be careful to avoid publicly sharing your license file!**\n",
    "\n",
    "We upload the file to `/tmp/mosek.lic` in order to help prevent accidental sharing of your license file.  It will be cleared at the end of each session.\n",
    "\n",
    "Note: If you share an active session with a collaborator with your license uploaded, they could potentially copy the file out of `/tmp` and obtain your license.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import os.path\n",
    "\n",
    "import ipywidgets as widgets  # Our use requires ipywidgets >= 7.5.0\n",
    "from IPython.display import display\n",
    "\n",
    "if \"MOSEKLM_LICENSE_FILE\" not in os.environ:\n",
    "    # If a mosek.lic file has already been uploaded, then simply use it here.\n",
    "    if os.path.exists('/tmp/mosek.lic'):\n",
    "        os.environ[\"MOSEKLM_LICENSE_FILE\"] = \"/tmp/mosek.lic\"\n",
    "    else:\n",
    "        uploader = widgets.FileUpload(accept='.lic', multiple=False)\n",
    "        display(uploader)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if \"MOSEKLM_LICENSE_FILE\" not in os.environ:\n",
    "    assert len(uploader.value.values()) > 0, \"Please upload a license file using the Upload widget above.\"\n",
    "    with open('/tmp/mosek.lic', 'wb') as output_file:\n",
    "        output_file.write(list(uploader.value.values())[-1]['content']) \n",
    "    os.environ[\"MOSEKLM_LICENSE_FILE\"] = \"/tmp/mosek.lic\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can confirm that Drake believes Mosek should now be available:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pydrake.solvers import MosekSolver\n",
    "\n",
    "print(MosekSolver().enabled())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's solve a trivial semidefinite program. \n",
    "\n",
    "$$\\begin{aligned} \\min_S \\quad & \\text{Trace}(S) \\\\ \\text{subject to} \\quad & S_{1,0} = 1, \\\\ & S \\succeq 0. \\end{aligned}$$\n",
    "\n",
    "The known optimal solution is $ S = \\begin{bmatrix} 1 & 1 \\\\ 1 & 1\\end{bmatrix}.$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pydrake.solvers import MathematicalProgram, MosekSolver\n",
    "\n",
    "prog = MathematicalProgram()\n",
    "S = prog.NewSymmetricContinuousVariables(2, \"S\")\n",
    "\n",
    "# S ≽ 0.\n",
    "prog.AddPositiveSemidefiniteConstraint(S)\n",
    "\n",
    "# S(1, 0) = 1\n",
    "prog.AddBoundingBoxConstraint(1, 1, S[1, 0])\n",
    "\n",
    "# min Trace(S)\n",
    "prog.AddLinearCost(S[0,0] + S[1,1])\n",
    "\n",
    "mosek = MosekSolver()\n",
    "result = mosek.Solve(prog)\n",
    "assert result.is_success()\n",
    "\n",
    "print(result.GetSolution(S))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Gurobi\n",
    "\n",
    "Gurobi is not yet available in the binary releases of Drake.  We hope to enable it in the future: https://github.com/RobotLocomotion/drake/issues/10804"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Clean up\n",
    "\n",
    "For good measure, let's delete any temporary license files now."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if os.path.exists('/tmp/mosek.lic'):\n",
    "    os.remove('/tmp/mosek.lic')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "b0fa6594d8f4cbf19f97940f81e996739fb7646882a419484c72d19e05852a7e"
  },
  "kernelspec": {
   "display_name": "Python 3.9.12 64-bit",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.12"
  },
  "orig_nbformat": 2
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
